package com.gaojinqi.sys.core.shiro.util;

import com.gaojinqi.base.common.util.UUIDUtil;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * Jwt 工具类
 *
 * @author gaojinqi
 * @version 1.0
 * @since 2020年05月09日
 */
@Slf4j
public class JwtUtil {

    public static String createId() {
        return UUIDUtil.getUuid();
    }

    /**
     * 创建token
     *
     * @param subject            主体，一般存用户名
     * @param param              参数
     * @param signatureAlgorithm 加密算法枚举
     * @param secret             密钥
     * @param expireTime         过期时间，单位：毫秒
     * @return 加密后的token
     */
    public static String createToken(String subject, Map<String, Object> param,
                                     SignatureAlgorithm signatureAlgorithm, String secret, Long expireTime) {
        return createToken(createId(), subject, param, signatureAlgorithm, secret, expireTime);
    }

    /**
     * 创建token
     *
     * @param id                 uuid
     * @param subject            主体，一般存用户名
     * @param param              参数
     * @param signatureAlgorithm 加密算法枚举
     * @param secret             密钥
     * @param expireTime         过期时间，单位：毫秒
     * @return 加密后的token
     */
    public static String createToken(String id, String subject, Map<String, Object> param,
                                     SignatureAlgorithm signatureAlgorithm, String secret, Long expireTime) {
        return Jwts.builder()
                // 自定义会话id，与缓存关联
                .setId(id)
                // 角色编号集合
                .setClaims(param == null ? new HashMap<>() : param)
                // 凭证颁发时间
                .setIssuedAt(new Date())
                // 主体
                .setSubject(subject)
                // token过期时间（毫秒级）
                .setExpiration(new Date(System.currentTimeMillis() + expireTime))
                // 加密算法、加密秘钥
                .signWith(signatureAlgorithm, secret)
                .compact();
    }

    /**
     * 解析成 Claims，报错则直接忽略
     */
    public static Claims getClaims(String token, String secret) {
        Claims claims = null;
        try {
            claims = verifyToken(token, secret);
        } catch (JwtException e) {
            log.error("jwt 解析 Claims 失败 errorMsg = {}", e.getMessage(), e);
        } catch (IllegalArgumentException iae) {
            log.error("jwt 解析 Claims 失败 errorMsg = IllegalArgumentException === {}", iae.getMessage(), iae);
        }
        return claims;
    }

    public static Claims verifyToken(String token, String secret)
            throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }


    /**
     * 获取jwt id
     */
    public static String getId(Claims claims) {
        if (claims == null) {
            return null;
        }
        return claims.getId();
    }

    /**
     * 获取jwt 主题
     */
    public static String getSubject(Claims claims) {
        if (claims == null) {
            return null;
        }
        return claims.getSubject();
    }

    /**
     * 获取jwt发布时间
     */
    public static Date getIssuedAt(Claims claims) {
        if (claims == null) {
            return null;
        }
        return claims.getIssuedAt();
    }

    /**
     * 获取jwt失效时间
     */
    public static Date getExpiration(Claims claims) {
        if (claims == null) {
            return null;
        }
        return claims.getExpiration();
    }

    /**
     * 验证token是否失效
     *
     * @return true:过期   false:没过期
     */
    public static boolean isExpired(Claims claims) {
        if (claims == null) {
            return true;
        }
        try {
            final Date expiration = getExpiration(claims);
            if (expiration == null) {
                return true;
            }
            return expiration.before(new Date());
        } catch (ExpiredJwtException expiredJwtException) {
            return true;
        }
    }

}
